package item;

import item.pickable.*;

import java.io.NotActiveException;
import java.util.*;

import program.*;
import program.commands.BasicCommand;
import attribute.*;

import org.omg.CosNaming.IstringHelper;
import be.kuleuven.cs.som.annotate.*;
import item.*;
import attribute.*;
import exceptions.*;


/** 
 * @invar A robot must at all time have a valid energy.
 * 			|	canHaveAsEnergy(getEnergy())
 * 
 * @invar	orientation 
 * 				The orientation of a robot must be a valid orientation.
 * 				| isValidOrientation(getOrientation())
 * 
 * @invar ...
 * 			|hasProperItemsHeld()
 * 
 * @invar ...
 * 			|isValidMaxEnergyOnRobot(MAX_ENERGY_ON_ROBOT)
 */ 
/**
 * @author John Breyssem	1Ba Informatica		r0303394
 * 
 * @author Lars Feyaerts	1Ba Informatica		r0296831
 * 
 * @version	1.3
 *
 */
@SuppressWarnings("unused")
public class Robot extends Item{
	private Orientation orientation;
	private Energy energy;
	private final static Energy MIN_ENERGY = new Energy(0, EnergyUnit.Wattsecond);
	private final static Energy MAX_ENERGY = new Energy(20000, EnergyUnit.Wattsecond);
	private Energy MAX_ENERGY_ON_ROBOT = new Energy(20000, EnergyUnit.Wattsecond);
	private static final Energy ENERGY_FOR_TURN = new Energy(100, EnergyUnit.Wattsecond);
	private final Energy ENERGY_FOR_MOVE = new Energy(500, EnergyUnit.Wattsecond);
	private static final Energy ENERGY_FOR_SHOOT = new Energy(1000, EnergyUnit.Wattsecond);
	private ArrayList<PickableItem> listOfItemsHeld = new ArrayList<PickableItem>();
	private Program program;
	Random generator = new Random();
	
	/**
	 * Initialise this new robot with given coordinates X and Y on the board, 
	 * 			facing the given orientation with the given amount of energy.
	 * 
	 * @param X
	 * 			X coordinate of the robot's place on the board
	 * 
	 * @param Y
	 * 			Y coordinate of the robot's place on the board
	 * 
	 * @param orientation
	 * 			The orientation of the robot, going from UP to LEFT (clockwise)
	 * 
	 * @param energy
	 * 			The amount of energy in Wattsecond the robot starts with
	 * 
	 * @pre 	The given initial energy in Wattseconds must be a valid energy for the robot.
	 * 			|canHaveAsEnergy(energy, Wattsecond) 	
	 * 
	 * @effect	 The coordinates of this robot are set in view of the
	 *          the board established by zero to Long.MAX_VALUE.
	 *       	|setValidPosition(X, Y, board)
	 * 
	 * @post	The orientation of the robot is set to the given direction
	 * 			| new.getOrientation().ordinal() == orientation
	 *  
	 * @post 	A new object Energy with given energy and given unit is made.
	 * 			|new.getEnergy() == new Energy(energy, EnergyUnit.Wattsecond)
 	 */
	@Raw
	public Robot(long X, long Y, int orientation, double energy, Board board){
		setValidPosition(X, Y, board);
		this.orientation = toOrientation(orientation);
		this.setEnergy(new Energy(energy, EnergyUnit.Wattsecond));
	}
	
	/**
	 * Initialises a robot with the given orientation and the given energy.
	 * 
	 * @param orientation
	 * 			The orientation to be set.
	 * @param energy
	 * 			The energy to be set.
	 * 
	 * @pre 	The given initial energy in Wattseconds must be a valid energy for the robot.
	 * 			|canHaveAsEnergy(energy, Wattsecond)
	 * 
	 * @post The orientation of the robot is set to the given direction
	 * 			| new.getOrientation().ordinal() == orientation
	 * 
	 * @post A new object Energy with given energy and given unit is made.
	 * 			|new.getEnergy() == new Energy(energy, EnergyUnit.Wattsecond)
	 * 
	 */
	@Raw
	public Robot(int orientation, double energy){
		assert this.canHaveAsEnergy(energy, EnergyUnit.Wattsecond);
		this.orientation = toOrientation(orientation);
		this.setEnergy(new Energy(energy, EnergyUnit.Wattsecond));
	}

	/**
	 * Initialise this robot as a new robot with 1 as orientation and 10000 as energy.
	 * 
	 * @effect The new robot is initialized with 1 as orientation and 10000 as its energy in Wattsecond
	 */
	@Raw
	public Robot() {
		this(1, 10000);
	}

	/**
	 * Converts the given integer orientation into an Orientation.
	 * 
	 * @param orientation
	 * 			The integer that must be converted.
	 * 
	 * @return The orientation is done modulo 4 and if it is still negative, it is added by 4.
	 * 			|orienation = orientation % 4
	 * 			|if(orientation < 0) orientation += 4
	 * 			if the orientation equals zero, the orientation is set up.
	 * 			|if(orientation == 0) then result == Orientationation.UP;
	 * 			if the orientation equals one, the orientation is set right.
	 * 			|if(orientation == 1) then result == Orientationation.RIGHT;
	 * 			if the orientation equals two, the orientation is set down.
	 * 			|if(orientation == 2) then result == Orientationation.DOWN;
	 * 			if the orientation equals three, the orientation is set left.
	 * 			|if(orientation == 3) then result == Orientationation.LEFT;
	 */
	public Orientation toOrientation(int orientation){
		orientation = orientation % 4;
		if(orientation < 0) orientation += 4;
		if(orientation == 0)
			return Orientation.UP;
		else if(orientation == 1)
			return Orientation.RIGHT;
		else if(orientation == 2)
			return Orientation.DOWN;
		else
			return Orientation.LEFT;
	}
	
	
	/**
	 * Checks whether the given orientation is a valid orientation.
	 * 
	 * @param orientation
	 * 			The orientation to be checked.
	 * @return False if and only of the given orientation does not excist.
	 * 			|result == (orientation != null)
	 * 		
	 */
	public static boolean isValidOrientation(Orientation orientation){
		return orientation != null;
	}
	
	
	/**
	 * Checks whether the given energy is a valid energy of the robot.
	 * 
	 * @param energy
	 * 			The energyDouble to check.
	 * @param unit
	 * 			The unit the energyDouble is in.
	 * 
	 * @return True if and only if the given energy is bigger than or equals MIN_ENERGY times 
	 * 			and is smaller than or equals the maximum amount of energy.
	 * 			|result ==  (new Energy(energy, unit).compareTo(getMinEnergy()) >= 0) 
	 *			|	&& (new Energy(energy, unit).compareTo(getMax_ENERGY_ON_ROBOT) <= 0);
	 */
	public boolean canHaveAsEnergy(Double energy, EnergyUnit unit) {
		return (new Energy(energy, unit).compareTo(getMinEnergy()) >= 0) 
				&& (new Energy(energy, unit).compareTo(this.getMAX_ENERGY_ON_ROBOT()) <= 0);
	}

	/**
	 * Checks whether the given energy is a valid energy for the robot.
	 * 
	 * @param energy
	 * 			The energy to be checked.
	 * 
	 * @return Return true if and only if canHaveAsEnergy(Double, EnergyUnit) is true.
	 * 			|result == (energy != null) 
	 * 			|	&& (this.canHaveAsEnergy(energy.getEnergyDouble(), energy.getEnergyUnit()))
	 * 		
	 */
	public boolean canHaveAsEnergy(Energy energy){
		return (energy != null) 
				&& (this.canHaveAsEnergy(energy.getEnergyDouble(), energy.getEnergyUnit()));
	}
	
	/**
	 * Returns the energy of this robot.
	 * 
	 * @return
	 * 		Returns the energy of this robot.
	 * @throws TerminatedObjectException
	 * 			If this robot is terminated.
	 * 			|this.isTerminated()
	 * @throws NoEnergyException
	 * 			If the robot's energy
	 * 			|this.isTerminated()
	 */
	@Basic @Raw
	public Energy getEnergy() throws TerminatedObjectException  {
		if(this.isTerminated())
			throw new TerminatedObjectException(this);
		return this.energy;
	}
	
	/**
	 * Returns the energy double of this robot in Wattsecond.
	 * 
	 * @return Returns the energyDouble of the energy of this robot set to a wattsecond energy.
	 * 			|result == this.getEnergy().toEnergy(EnergyUnit.Wattsecond).getEnergyDouble()
	 */
	public double getEnergyDoubleInWattsecond(){
		return this.getEnergy().toEnergy(EnergyUnit.Wattsecond).getEnergyDouble();
	}	
	
	/**
	 * Sets the robot's energy to the given energy.
	 * 
	 * @param energy
	 * 			The energy to be set.
	 * 
	 * @pre The given energy must be a valid energy.
	 * 			|this.canHaveAsEnergy(energy)
	 * 
	 * @post The new energy of this robot is the same as the given energy.
	 * 			|new.getEnergy() == energy 
	 * 
	 */
	@Raw
	public void setEnergy(Energy energy){
		assert(this.canHaveAsEnergy(energy));
		this.energy = energy;
	}
	
	/**
	 * Returns the orientation of this robot.
	 * 
	 * @return
	 * 		Returns the orientation of this robot.
	 */
	@Basic
	public Orientation getOrientation() throws TerminatedObjectException{
		return this.orientation;
	}
	
	/**
	 * Sets the orientation of this robot to the given orientation
	 * 
	 * @param orientation
	 * 			The orientation the robot must point to.
	 * 	
	 * @post The new orientation of this robot equals the given orientation.
	 * 			|new.getOrientation() == orientation
	 * @throws IllegalArgumentException
	 * 			This robot cannot accept the given orientation as its orientation.
	 * 			| orientation == null
	 * @throws TerminatedObjectException exc
	 * 			This robot must not be terminated
	 * 			|this.isTerminated()
	 */
	public void setOrientation(Orientation orientation) throws IllegalArgumentException, TerminatedObjectException{
		try{
			if(orientation == null)
				throw new IllegalArgumentException();
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			else
				this.orientation = orientation;
		}
		catch(IllegalArgumentException exc){
			assert(orientation == null);
			System.out.println("The orientation must not be null!");
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			System.out.println("This item " + exc.getObject() + " is terminated.");
		}
		
	}
	
	/**
	 * Decrease a given amount of energy of this robot.
	 * 
	 * @param energy
	 * 			The energy to be decreased.
	 * 
	 * @pre		This robot can accept the given amount of energy for subtraction.
	 * 			|this.canDecreaseEnergy(energy)
	 * 
	 * @post The new energy of this robot is equal to the old energy of this robot decremented 
	 * 				with the given amount of energy.
	 * 			|new.getEnergy() == this.getEnergy().subtract(energy);
	 */
	public void decreaseEnergy(Energy energy) {
		this.setEnergy(this.getEnergy().subtract(energy));
	}
	
	/**
	 * Return a boolean reflecting whether this robot can accept 
	 * 		the given amount of energy of the given EnergyUnit for subtraction.
	 * 
	 * @param energy
	 * 			The energy to be checked.
	 * 
	 * @effect	True if and only if the given amount of energy is valid 
	 * 				and if the energy of this robot decremented with the given amount of energy
	 * 				is a valid energy for any robot.
	 * 			|result ==  canHaveAsEnergy(this.getEnergy().subtract(energy))
	 */
	public boolean canDecreaseEnergy(Energy energy){
		return (energy != null) && (this.canHaveAsEnergy(this.getEnergy().subtract(energy)));
	}
	
	/**
	 * Add a given amount of energy to the this robot's energy.
	 * 
	 * @param energy
	 * 			The amount of energy to be added up.
	 * 
	 * @pre 	The robot can accept the given amount of energy for the addition.	
	 * 			|canRecharge(energy)
	 * 
	 * @post	The new energy of this robot is equal to the old energy of this robot added up with 
	 * 				the given amount of energy.
	 * 			| new.getEnergy() == this.getEnergy().add(energy);
	 */
	public void recharge(Energy energy){	
		this.setEnergy(this.getEnergy().add(energy));
	}
	
	
	/**
	 * Return a boolean reflecting whether this robot can accept the given amount of energy for addition.
	 * 
	 * @param energy
	 * 			The energy amount to be checked.
	 * 
	 * @return	True if and only if the given amount of energy is valid 
	 * 				and if the energy of this robot added up with the given amount of energy
	 * 				 is a valid energy for any robot.
	 * 			|result == (energy != null) && (this.canHaveAsEnergy(this.getEnergy().add(energy)))
	 */
	public boolean canRecharge(Energy energy){
		return (energy != null) && (this.canHaveAsEnergy(this.getEnergy().add(energy)));
	}
	
	/**
	 * 	Returns the constant energy that is needed for a move.
	 */
	@Immutable
	public Energy getEnergyForMove(){
		return this.ENERGY_FOR_MOVE;
	}
	
	/**
	 * Returns the maximum energy any robot can have.
	 */
	@Immutable
	public static Energy getMaxEnergy(){
		return MAX_ENERGY;
	}

	/**
	 * @return the mAX_ENERGY_ON_ROBOT
	 */
	public Energy getMAX_ENERGY_ON_ROBOT() {
		return this.MAX_ENERGY_ON_ROBOT;
	}
	
	/**
	 * 	Returns the MIN_ENERGY of the robot.
	 */
	@Immutable
	public static Energy getMinEnergy(){
		return MIN_ENERGY;
	}

	/**
	 * Sets the max energy this robot can have.
	 * 
	 * @param MAX_ENERGY_ON_ROBOT 
	 * 			The energy to be set.
	 * 
	 * @pre The energy must be a valid energy
	 * 			|isValidMaxEnergyOnRobot(MAX_ENERGY_ON_ROBOT)
	 * 
	 * @post The new MAX_ENERGY_ON_ROBOT equals the given MAX_ENERGY_ON_ROBOT.
	 * 			|new.getMAX_ENERGY_ON_ROBOT() == MAX_ENERGY_ON_ROBOT
	 */
	@Raw
	public void setMAX_ENERGY_ON_ROBOT(Energy MAX_ENERGY_ON_ROBOT) {
		this.MAX_ENERGY_ON_ROBOT = MAX_ENERGY_ON_ROBOT;
	}
	
	/**
	 * Check whether this maxEnergyOnRobot is a valid energy for a robot.
	 * 
	 * @param maxEnergyOnRobot
	 * 			The energy to check
	 * 
	 * @return Returns true if and only if the given energy is bigger than or equals the minimum energy
	 * 				for a robot, and smaller than or equals the maximun energy for a robot.
	 * 			|( (maxEnergyOnRobot != null) && (maxEnergyOnRobot.compareTo(getMinEnergy()) >= 0) 
	 *			|	&& (maxEnergyOnRobot.compareTo(getMaxEnergy()) <= 0))
	 */
	public static boolean isValidMaxEnergyOnRobot(Energy maxEnergyOnRobot){
		return ( (maxEnergyOnRobot != null) && (maxEnergyOnRobot.compareTo(getMinEnergy()) >= 0) 
				&& (maxEnergyOnRobot.compareTo(getMaxEnergy()) <= 0));
	}
	
	
	/**
	 * Check whether this robot can have the given pickable item as one of its items held.
	 * 
	 * @param item
	 * 			The item to check.
	 * @return  False if the given Pickable item is not effective, the given item or this robot is terminated 
	 * 				or when the item is already in the list.
	 * 			|result == ( (item != null) && !(item.isTerminated()) 
	 *			|	&& !(this.isTerminated()) && this.getListOfItemsHeld().contains(item) == false )
	 */
	@Raw
	public boolean canHaveAsItemHeld(PickableItem item){
		if(item.isTerminated())
			this.getListOfItemsHeld().remove(item);
		return( (item != null) && (!this.isTerminated())
				&& !(this.isTerminated()) && this.getListOfItemsHeld().contains(item) == false );
	}
	
	/**
	 * Check whether this robot has proper items held.
	 * 
	 * @return True if and only if this robot can have each of its items held as an item held.		
	 * 			|result ==
	 * 			| 	for each item in getListOfItemsHeld()
	 * 			|		canHaveAsItemHeld(item)
	 */
	@Raw
	public boolean hasProperItemsHeld(){
		for(PickableItem item : this.getListOfItemsHeld())
			if(!this.canHaveAsItemHeld(item))
				return false;
		return true;
	}

	/**
	 * Moves this robot one step forward, taking the robot's orientation in account.
	 * 
	 * @pre The robot must have a position on a board in order to move forward. If not the method wil not be executed.
	 * 			|this.getPosition() != null
	 * 
	 * @pre 	The robot must have enough energy to move forward taking in account the weight of the robot's items held.
	 * 			|canDecreaseEnergy(this.getEnergyForMove().add(new Energy((gram/20), EnergyUnit.Wattsecond)))
	 * 
	 * @effect	If the new position of the robot is a valid position,
	 * 				the robot's positions change taking in account the orientation of the robot.
	 * 			| if (isValidposition(new.this.().getX(), new.this.getPosition().getY())) 
	 * 			|		this.changeToPosition(this.new.getPosition().getX(), this.new.getPosition().getY(), this.getPosition().getBoard());
	 * 
	 * @post	The robot can only move one step forward per move()
	 * 			|Math.abs(Math.abs(this.position.getX() + this.position.getY()) - Math.abs(this.position.getX() + this.position.getY())) == 1
	 * 
	 * @throws TerminatedObjectException(this)
	 * 			This robot is not terminated
	 * 			|this.isTerminated()
	 * 
	 * 
	 */
	public void move() throws TerminatedObjectException {
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			double gram = 0;
			for(PickableItem item: this.getListOfItemsHeld())	
				gram += item.getWeight();
			if(this.canDecreaseEnergy(this.getEnergyForMove().add(new Energy((gram/20), EnergyUnit.Wattsecond)))){
				switch (this.getOrientation()){
				case UP:	if (isValidPosition(this.getPosition().getX(), this.getPosition().getY()-1, this.getPosition().getBoard())){ 
					this.changeToPosition(this.getPosition().getX(), this.getPosition().getY()-1, this.getPosition().getBoard());}
				break;
				case RIGHT:	if (isValidPosition(this.getPosition().getX()+1, this.getPosition().getY(), this.getPosition().getBoard())){ 
					this.changeToPosition(getPosition().getX()+1, this.getPosition().getY(), this.getPosition().getBoard());}
				break;
				case DOWN:	if (isValidPosition(this.getPosition().getX(), this.getPosition().getY()+1, this.getPosition().getBoard())){ 
					this.changeToPosition(this.getPosition().getX(), this.getPosition().getY()+1, this.getPosition().getBoard());}
				break;
				case LEFT:	if (isValidPosition(this.getPosition().getX()-1, this.getPosition().getY(), this.getPosition().getBoard())){ 
					this.changeToPosition(this.getPosition().getX()-1, this.getPosition().getY(), this.getPosition().getBoard());}
				break;
				}
			}
		}
		catch(IllegalStateException exc){
			System.out.println("The item has no position, therefore it can not be moved!");
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			System.out.println("The item " + exc.getObject() + " is terminated and cannot move");
		}
	}
	

	
	/**
	 * Turns this robot in a clockwise direction.
	 * 
	 * @pre	The robot must be on a position on a board.
	 * 			|this.getPosition() != null
	 * 
	 * @pre		The robot must have enough energy to turn clockwise.
	 * 			|canDecreaseEnergy(ENERGY_FOR_TURN)
	 * 
	 * @effect 	The robot's orientation is changed so that the robot has turned clockwise taking in account 
	 * 				the former orientation of the robot.	
	 * 			|if(this.orientation.ordinal() == 3)
	 * 			|	then this.new.orientation.ordinal() == 0
	 * 			|else
	 * 			|	this.new.orientation.ordinal() = this.orientation.ordinal() +1
	 * @throws NotOnAPositionException(this)
	 * 			This robot's position equals null
	 * 			|this.getPosition() == null
	 * @throws TerminatedObjectException(this)
	 * 			This robot is not terminated
	 * 			|this.isTerminated()
	 */
	public void turnClockWise() throws NotOnAPositionException, TerminatedObjectException{
		try{
			if(this.getPosition() == null)
				throw new NotOnAPositionException(this);
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			else{
				switch(this.getOrientation()){
				case UP: this.orientation = Orientation.RIGHT; break;
				case RIGHT: this.orientation = Orientation.DOWN; break;
				case DOWN: this.orientation = Orientation.LEFT; break;
				case LEFT: this.orientation = Orientation.UP; break;
				}
				decreaseEnergy(ENERGY_FOR_TURN);
			}
		}
		catch(NotOnAPositionException exc){
			assert(this.getPosition() == null);
			System.out.println("The item " + exc.getItem() + " has no position, therefore it can not be turned!");			
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			System.out.println("The item " + exc.getObject() + " is terminated and cannot turn");
		}
		catch(IllegalStateException exc) {
			System.out.println("You cannot turn this item");
		}
		
	}

	/**
	 * Turns this robot in a counterclockwise direction.
	 * 
	 * @pre	The robot must be on a position in a board.
	 * 			|this.getPosition() != null
	 * 
	 * @pre		The robot must have enough energy to turn counterclockwise.
	 * 			|canDecreaseEnergy(ENERGY_FOR_TURN)
	 * 
	 * @effect The robot's orientation is changed so that the robot has turned counterclockwise taking in account 
	 * 				the former orientation of the robot.	
	 * 			|if(this.orientation.ordinal() == 0)
	 * 			|	then this.new.orientation.ordinal() == 3
	 * 			|else
	 * 			|	this.new.orientation.ordinal() = this.orientation.ordinal() -1
	 * 
	 * @throws NotOnAPositionException(this)
	 * 			This robot's position equals null
	 * 			|this.getPosition() == null
	 * @throws TerminatedObjectException(this)
	 * 			This robot is terminated
	 * 			|this.isTerminated()
	 */
	public void turnCounterClockWise() throws NotOnAPositionException, TerminatedObjectException{
		try{
			if(this.getPosition() == null)
				throw new NotOnAPositionException(this);
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			else {
				switch(this.getOrientation()){
				case UP: this.orientation = Orientation.LEFT; break;
				case RIGHT: this.orientation = Orientation.UP; break;
				case DOWN: this.orientation = Orientation.RIGHT; break;
				case LEFT: this.orientation = Orientation.DOWN; break;
				}
				decreaseEnergy(ENERGY_FOR_TURN);
			}
		}
		catch(NotOnAPositionException exc){
			assert(this.getPosition() == null);
			System.out.println("The item " + exc.getItem() + " has no position, therefore it can not be turned!");			
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			System.out.println("The item " + exc.getObject() + " is terminated and cannot turn");
		}
		catch(IllegalStateException exc) {
			System.out.println("You cannot turn this item");
		}
		
	}

	/**
	 * Set the orientation of this robot in the given orientation.
	 * 
	 * @param orientation
	 * 			The orientation the robot must be moved to.
	 * 
	 * @effect This robot's orientation is set to the given orientation 
	 * 				using the methods turnClockWise() and turnCounterClockWise(). 
	 * 				The calculation is based on the difference of 
	 * 				the ordinal of the given orientation and the ordinal of this robot's orientation.
	 * 				|new.getOrientation() == orientation
	 * 
	 * @throws IllegalArgumentException()
	 * 			orientation must not be null.
	 * 			|!isValidOrientation(orientation)
	 * @throws TerminatedObjectException(this)
	 * 			This robot is terminated
	 * 			|this.isTerminated()
	 * @throws NotOnAPositionException(this)
	 * 			This robot's position equals null
	 * 			|this.getPosition() == null
	 */
	public void setOrientationTo(Orientation orientation) throws IllegalArgumentException, TerminatedObjectException, NotOnAPositionException{
		try{
			if(!isValidOrientation(orientation))
				throw new IllegalArgumentException();
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(this.getPosition() == null)
				throw new NotOnAPositionException(this);
			int initialOrientation = this.getOrientation().ordinal();
			int newOrientation = orientation.ordinal();
			int difference = newOrientation - initialOrientation;
			if(difference == -3 || difference == 1)
				this.turnClockWise();
			else if(difference == -1 || difference == 3)
				this.turnCounterClockWise();
			else if(Math.abs(difference) == 2){
				this.turnClockWise(); 
				this.turnClockWise();
			}
		}
		catch(IllegalArgumentException exc){
			assert (!isValidOrientation(orientation));
			System.out.println("The method setOrienatationTo cannot set to an invalid orientation");
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			System.out.println("The item " + exc.getObject() + " is terminated and cannot turn");
		}
		catch(NotOnAPositionException exc){
			assert(this.getPosition() == null);
			System.out.println("The item " + exc.getItem() + " has no position, therefore it can not be turned!");			
		}
	}

	/**
	 * Returns the amount of energy in wattsecond a robot needs to get to a given position (x, y).
	 * 
	 * @param x
	 * 		The given x-coordinate of the position the robot needs to move to.
	 * 
	 * @param y
	 * 		The given y-coordinate of the position the robot needs to move to.
	 * 
	 * @pre		There cannot be any obstacles on the Manhattan distance
	 * 				between this robot and the given coordinates
	 * 			|this.areNoObstaclesOnManhattanDistance(long X, long Y)
	 * 
	 * @return	If this robot's board is null, return -1
	 * 			|if(this.getPosition().getBoard() == null) return -1;
	 * 
	 * @return	Returns -2 if this robot does not have enough energy to reach the given position.
	 * 			|if(((ENERGY_FOR_MOVE.add(new Energy(gram/20, EnergyUnit.Wattsecond))).times(amountOfMovesX+amountOfMovesY) )
	 *				.toEnergy(EnergyUnit.Wattsecond).compareTo(this.getEnergy) > 0)
	 *			|then  return -1;
	 *			
	 * @return	Returns the minimal energy required to reach the given position (x, y) taking only in account the moves to get there.
	 * 			| result == ((ENERGY_FOR_MOVE.add(new Energy(gram/20, EnergyUnit.Wattsecond))).times(amountOfMovesX+amountOfMovesY) )
	 *				.toEnergy(EnergyUnit.Wattsecond).getEnergyDouble()
	 * @throws TerminatedObjectException(this)
	 * 			This robot is terminated
	 * 			|this.isTerminated()
	 */
	public double getEnergyRequiredToReach(long x, long y) throws TerminatedObjectException{
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			assert(!this.areObstaclesOnManhattanDistance(x, y));
			int amountOfMovesX = (int) Math.abs(this.getPosition().getX() - x);
			int amountOfMovesY = (int) Math.abs(this.getPosition().getY() - y);
			int gram = 0;
			for(Item item: this.getListOfItemsHeld())	gram += ((Battery)item).getWeight();
			if(( ( (ENERGY_FOR_MOVE.add(new Energy(gram/20, EnergyUnit.Wattsecond))).times(amountOfMovesX+amountOfMovesY) )
					.compareTo(this.getEnergy())) > 0)
				return -1;
			else{
				return ((ENERGY_FOR_MOVE.add(new Energy(gram/20, EnergyUnit.Wattsecond))).times(amountOfMovesX+amountOfMovesY) )
						.toEnergy(EnergyUnit.Wattsecond).getEnergyDouble();
			}
		}
		catch(TerminatedObjectException exc){
				assert(this.isTerminated());
				System.out.println("The item " + exc.getObject() + " is terminated and so no required energy could be calculated.");
				return -1;
		}
		catch(IllegalStateException exc){
			System.out.println("The item has no position and so no required energy could be calculated");
			return -1; // dit is als de robot niet op een bord staat( == if(this.getPosition().getBoard() == null) return -1;)
		}
	}
	
	
	
	/**
	 * Checks whether the rectangle between this and the given X and Y has a robot or a wal on it.
	 * 
	 * @param X
	 * 			The X coordinate the rectangle depends on.
	 * @param Y
	 * 			The Y coordinate the rectangle depends on.
	 * @return False if and only if every position on the board is not in the rectangle 
	 * 				|for each position in this.getPosition().getBoard().getListOfPositions()
	 * 				|	( X >= this.getPosition().getX() ? position.getX() >= this.getPosition().getX() && position.getX() <= X 
	 *				|		: position.getX() >= X	&& position.getX() <= this.getPosition().getX() )
	 *				|	&& ( Y >= this.getPosition().getY() ? position.getY() >= this.getPosition().getY() 	&& position.getY() <= Y 
	 *	 			|		: position.getY() >= Y	&& position.getY() <= this.getPosition().getY() )
	 *			or has no robot or wall in its position.
	 * 				|(position.hasItemOfClass(Robot.class) || position.hasItemOfClass(Wall.class)) )
	 * 					unless the position equals this position or the given coordinates 
	 *						|((this.getPosition().getX() == position.getX() || this.getPosition().getY() == position.getY())
	 *						|		&& (X == position.getX() && Y == position.getY()))
	 */
	private boolean areObstaclesOnManhattanDistance(long X, long Y) {
			for(Position position : this.getPosition().getBoard().getListOfPositions()){
				if( ( X >= this.getPosition().getX() ? position.getX() >= this.getPosition().getX() && position.getX() <= X 
						: position.getX() >= X	&& position.getX() <= this.getPosition().getX() )
						&& ( Y >= this.getPosition().getY() ? position.getY() >= this.getPosition().getY() 	&& position.getY() <= Y 
						: position.getY() >= Y	&& position.getY() <= this.getPosition().getY() )
						&& (!(this.getPosition().getX() == position.getX() && this.getPosition().getY() == position.getY())
								&& !(X == position.getX() && Y == position.getY()))
								&& (position.hasItemOfClass(Robot.class) || position.hasItemOfClass(Wall.class)) )
					return true;
			}
			return false;
		
	}

	/**
	 * Checks whether this robot is next to the given robot.
	 * 
	 * @param robot
	 * 			The robot that this robot has to be compared with.
	 * 
	 * @return
	 * 		Returns true if the two robots are next to each other
	 * 		|result == ((Math.abs(this.position.getX() - robot.position.getX()) + Math.abs(this.position.getY() - robot.position.getY())) == 1)
	 * 		|	&& this.getPosition().getBoard() equals robot.getPosition().getBoard()
	 */
	private boolean isNextTo(Robot robot){
			return ((Math.abs(this.getPosition().getX() - robot.getPosition().getX()) + Math.abs(this.getPosition().getY() - robot.getPosition().getY())) == 1) 
					&& this.getPosition().getBoard() == robot.getPosition().getBoard();		
	}
	
	/**
	 * Sets this robot's orientation and the given robot's orientation towards each other.
	 * 
	 * @param robot
	 * 			This robot's orientation must point to the given robot and the given robot's orientation must point to this robot. 
	 * 
	 * @effect	This robot's orientation is set towards the given robot.
	 * 			|this.setOrientation(orientation);
	 * 
	 * @effect 	The given robot's orientation is set towards this robot.
	 * 			|this.setOrientation(toOrientation(orientation.ordinal()+2))
	 */
	private void setOrientationTowardsEachother(Robot robot){
		if(this.getPosition().getX() < robot.getPosition().getX()){
			this.setOrientationTo(Orientation.RIGHT);
			robot.setOrientationTo(Orientation.LEFT);
		}
		else if(this.getPosition().getX() > robot.getPosition().getX()){
			this.setOrientationTo(Orientation.LEFT);
			robot.setOrientationTo(Orientation.RIGHT);
		}
		else if(this.getPosition().getX() == robot.getPosition().getX()){
			if(this.getPosition().getY() < robot.getPosition().getY()){
				this.setOrientationTo(Orientation.DOWN);
				robot.setOrientationTo(Orientation.UP);
			}
			else if(this.getPosition().getY() > robot.getPosition().getY()){
				this.setOrientationTo(Orientation.UP);
				robot.setOrientationTo(Orientation.DOWN);
			}
		}
	}
	
	/**
	 * Moves this robot next to the given robot using as less energy as possible using an equivalent Manhattan distance.
	 * 
	 * @param robot
	 * 			The given robot this robot must be moved next to.
	 * 
	 * @pre		There cannot be any obstacles on the Manhattan distance
	 * 				between this robot and the given coordinates
	 * 			|this.areNoObstaclesOnManhattanDistance(robot.getPosition().getX(), robot.getPosition().getY());
	 * 
	 * @effect	The method is only executed if one of the two robots has enough energy to move forward
	 * 				and if the two robots are not yet next to each other.
	 * 			|(!this.isNextTo(robot)) && ((this.getEnergy().compareTo(ENERGY_FOR_MOVE) >= 0)
	 *			|	||(robot.getEnergy().compareTo(ENERGY_FOR_MOVE) >= 0))
	 * 
	 * @effect	The robots are turned in such a way that they are always pointing to each other 
	 * 				when a robot sets a move forward.
	 * 			|this.setOrientationTowardsEachother(robot)
	 * 
	 * @effect	If their X-coordinates differ, the robots move forward one by one recursively so that the absolute value of the difference 
	 * 				of the X-coordinates of the two robots becomes smaller (or stays the same)
	 * 				until they reach the point where their X-coordinates are equal
	 * 				or until they stand next to each other or until no robot has enough energy to move. 
	 * 			The robots are now moved closer to each other or are next to each other.
	 *			|	this.getEnergyToReach(robot.new.getPosition().getX(), robot.new.getPosition().getY()) 
	 *			|	=< this.getEnergyToReach(robot.getPosition().getX(), robot.getPosition().getY()) //= for when -1 is returned
	 *
	 * @effect	Every move the robots make is the most efficient move to get closer to each other.
	 * 			| if robot.move() 
	 * 			|	then (Math.abs(this.position.getX() - robot.position.getX()) > Math.abs(this.new.getX() - robot.new.getX()))
	 * 			|	|| (Math.abs(this.position.getY() - robot.position.getY()) > Math.abs(this.new.getY() - robot.new.getY())

	 * @throws IllegalArgumentException()
	 * 			The given robot equals null
	 * 			|robot == null
	 * @throws TerminatedObjectException(this || robot)
	 * 			The robot is terminated
	 * 			|this.isTerminated() || robot.isTerminated
	 */
	public void moveNextTo(Robot robot)throws TerminatedObjectException, IllegalArgumentException{
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(robot.isTerminated())
				throw new TerminatedObjectException(robot);
			if(robot == null)
				throw new IllegalArgumentException();
			if(!this.areObstaclesOnManhattanDistance(robot.getPosition().getX(), robot.getPosition().getY())){ //if this.position == null, it will throw exc in method and search here for catcher.
				if((!this.isNextTo(robot)) && ((this.getEnergy().compareTo(ENERGY_FOR_MOVE) >= 0) 
						||(robot.getEnergy().compareTo(ENERGY_FOR_MOVE) >= 0))){
					this.setOrientationTowardsEachother(robot);
					robot.move();
					robot.moveNextTo(this);
				}
			}
		}
		catch(TerminatedObjectException exc){
			assert(this.isTerminated());
			assert(robot.isTerminated());
			System.out.println("The item " + exc.getObject() + " is terminated could not be moved next to eny robot");
		}
		catch(IllegalStateException exc){
			System.out.println("One of the items has no position!");
		}
		catch(IllegalArgumentException exc){
			assert (robot == null);
			System.out.println("The robot to move next to must excist");
		}
	}
	
	/**
	 * Returns the amount of energy as a fraction of the maximum amount.
	 */
	public double fractionOfEnergy(){
		return this.getEnergy().toEnergy(this.getMAX_ENERGY_ON_ROBOT().getEnergyUnit()).getEnergyDouble() 
				/ this.getMAX_ENERGY_ON_ROBOT().getEnergyDouble() * 100;
	}
	
	/**
	 * A robot shoots its laser in the direction it is facing thereby consuming 1000Ws.
	 * 
	 * @pre This robot can decrease Energy for shoot.
	 * 			|this.canDecreaseEnergy(ENERGY_FOR_SHOOT)
	 * 
	 * @effect In the first position that is encountered by the laser, a random item is selected
	 * 				and hit by that laser
	 * 			|Item item = position.getListOfItemsOnPosition.get(random)
	 * 			|	item.hitByLaser()
	 * 
	 * 
	 * @throws TerminatedObjectException
	 * 			this robot is terminated
	 * 			|this.isTerminated()
	 * 
	 */
	public void shoot() throws TerminatedObjectException{
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if (this.canDecreaseEnergy(ENERGY_FOR_SHOOT)){
				boolean killed = false;
				switch(this.getOrientation()){
				case UP:
					for(long y = this.getPosition().getY() -1; isValidPosition(this.getPosition().getX(), y, this.getPosition().getBoard()); y--){
						Position p = this.doesPositionAlreadyExist(this.getPosition().getX(), y, this.getPosition().getBoard());
						killed = this.shootMethod(p);
						if(killed) break;
					}
					break;
				case RIGHT:
					for(long x = this.getPosition().getX() +1; isValidPosition(x, this.getPosition().getY(), this.getPosition().getBoard()); x++){
						Position p = this.doesPositionAlreadyExist(x, this.getPosition().getY(), this.getPosition().getBoard());
						killed = this.shootMethod(p);
						if(killed) break;
					}
					break;
				case DOWN: 
					for(long y = this.getPosition().getY() +1; isValidPosition(this.getPosition().getX(), y, this.getPosition().getBoard()); y++){
						Position p = this.doesPositionAlreadyExist(this.getPosition().getX(), y, this.getPosition().getBoard());
						killed = this.shootMethod(p);
						if(killed) break;
					}
					break;
				case LEFT: 
					for(long x = this.getPosition().getX() -1; isValidPosition(x, this.getPosition().getY(), this.getPosition().getBoard()); x--){
						Position p = this.doesPositionAlreadyExist(x, this.getPosition().getY(), this.getPosition().getBoard());
						killed = this.shootMethod(p);
						if(killed) break;
					}
				}		
			}
		}
		catch(IllegalStateException exc){
			System.out.println("This item is not on a board");
		}
		catch(TerminatedObjectException exc){
			System.out.println("A terminated robot cannot shoot.");
		}
	}
	
	/**
	 * Helpmethod for shoot().
	 * 
	 * @param p
	 * 			The position the items are on.
	 * @return True if and only id p != null and item.hitByLaser() is executed.
	 * 			|if(p!= null)
	 * 			|	result == true
	 */
	private boolean shootMethod(Position p){
		if(p != null){
			Random generator = new Random();
			int random = generator.nextInt(p.getListOfItemsAtPosition().size());//between 0 and one less than the parameter.
			Item item = p.getListOfItemsAtPosition().get(random);
			item.hitByLaser();
			return true;
		}
		else
			return false;
	}
	
	/**
	 *This robot uses the given PickableItem.
	 * 
	 *@effect If the item is a battery, the robot's energy is recharged with the battery's energy untill
	 *			it is full or until the battery has no more energy left. 
	 *			|if(item.getClass() == Battery.class){
	 * 			|	new.getEnergy() == this.getEnergy() + item.getEnergy()
	 * 			| 	|| new.getEnergy() == this.getMaxEnergyOnRobot()
	 * 			|)
	 * 
	 * @effect If the item is a repairkit, the robot's max energy on robot increases with half the amount
	 * 			of energy in the repairkit or untill the max energy for any robot is reached.	
	 * 			|if(item.getClass() == RepairKit.class{
	 * 			|	new.getMaxEnergyOnRobot() == this.getMaxEnergyOnRobot() + item.getEnergy()/2
	 * 			|	|| new.getMaxEenrgyOnRobot() == getMaxEnergy()
	 * 			|}
	 * 
	 * @effect If the item is a surprisebox, it can do 5 things: 
	 * 			explode, where the robot is terminated together with the box,
	 * 			|this.isTerminated == true && item.isTerminated == true
	 * 			or teleported to a random location on the board,
	 * 			|new.getPosition == this.setValidPosition(generator.nextInt((int) this.getPosition().getBoard().getWidth())
	 *			|				, generator.nextInt((int)this.getPosition().getBoard().getHeight())
	 *			|				, this.getPosition().getBoard())
	 *			or the surprisebox transformes in one of three Pickable Items
	 *			|PickableItem content = ((SurpriseBox)item).surpriseBoxContent();
	 *			|	this.pickup(content)
	 * 
	 * @throws IllegalArgumentException
	 * 			The given item does not exist
	 * 			|item == null
	 * @throws TerminatedObjectException
	 * 			This or the given item are terminated
	 * 			|this.isTerminated() || item.isTerminated()
	 * @Throws NotOnAPositionException
	 * 			This position does not exist
	 * 			|this.getPosition() == null
	 * 		
	 */
	public void use(PickableItem item) throws IllegalArgumentException, TerminatedObjectException, NotOnAPositionException{
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(!this.getListOfItemsHeld().contains(item))
				throw new IllegalArgumentException("notThere");
			if(item.isTerminated())
				throw new TerminatedObjectException(item);
			if(item == null)
				throw new IllegalArgumentException();
			if(this.getPosition() == null)
				throw new NotOnAPositionException(this);
			if (item.getClass() == Battery.class){
				if((((Battery)item).getEnergy().compareTo(this.getMAX_ENERGY_ON_ROBOT().subtract(this.getEnergy())) <= 0)){
					this.recharge(((Battery)item).getEnergy());
					((Battery)item).drainEnergy(((Battery)item).getEnergy());//methode transferEnergy maken
					this.drop(((Battery)item));
					((Battery)item).terminate();
				}
				else{
					Energy energy = this.getMAX_ENERGY_ON_ROBOT().subtract(this.getEnergy());
					this.recharge(energy);
					((Battery)item).drainEnergy(energy);
				}
			}
			else if (item.getClass() == RepairKit.class){
				if(((((RepairKit)item).getEnergy().divide(2)).compareTo(MAX_ENERGY.subtract(this.getMAX_ENERGY_ON_ROBOT())) <= 0)){
					this.setMAX_ENERGY_ON_ROBOT(((RepairKit)item).getEnergy().divide(2).add(this.getMAX_ENERGY_ON_ROBOT()));
					((RepairKit)item).drainEnergy(((RepairKit)item).getEnergy());
					this.drop(((RepairKit)item));
					((RepairKit)item).terminate();
				}
				else{
					Energy energy = MAX_ENERGY.subtract(this.getMAX_ENERGY_ON_ROBOT());
					this.setMAX_ENERGY_ON_ROBOT(MAX_ENERGY);
					((RepairKit)item).drainEnergy(energy.times(2));
				}
			}
			else if (item.getClass() == SurpriseBox.class){
				PickableItem content = this.surpriseBoxContent(((SurpriseBox)item));
				if(content != null){
					this.pickUp(content);
					this.drop(item);
					item.terminate();
				}
				else{
					int random = generator.nextInt(2);
					if(random == 0){ //explode
						this.drop(item);
						item.terminate();
						this.terminate();
					}
					else{ //teleport
						this.setValidPosition(generator.nextInt((int) this.getPosition().getBoard().getWidth())
								, generator.nextInt((int)this.getPosition().getBoard().getHeight())
								, this.getPosition().getBoard());
						this.drop(item);
						item.terminate();
					}
				}
			}
		}
		catch(TerminatedObjectException exc){
			if(exc.getObject() == item){
				System.out.println("The item you want to use is terminated");
				this.getListOfItemsHeld().remove(item);
			}
			else
				System.out.println("This robot is terminated and can't use any item");
		}
		catch(NotOnAPositionException exc){
			System.out.println("this item is not on a board and so cannot use pickable items");
		}
		catch(IllegalStateException exc){
			System.out.println("This robot is not on a board so using an item is impossible");
		}
		catch(IllegalArgumentException exc){
			if(exc.getMessage() == "notThere")
				System.out.println("The robot does not hold this item.");
			else
				System.out.println("This pickableItem does not exist!");
		}
	}
	

	/**
	 * Chooses one of 5 possibilities a surpriseBox can do.
	 * 
	 * @return ...
	 * 			|result == new Battery(this.getPosition().getX(), this.getPosition().getY(), 
	 *			|		(((random.nextInt(50)) + 1) * 100), this.getWeight(), this.getPosition().getBoard())
	 *	  		or
	 *			|result ==  new SurpriseBox(this.getPosition().getX(), this.getPosition().getY(),
	 *			|	this.getWeight(), this.getPosition().getBoard())	
	 *			or
	 *			|result == new RepairKit(this.getPosition().getX(), this.getPosition().getY(),
	 *			|	(((random.nextInt(50)) + 1) * 100), this.getWeight(), this.getPosition().getBoard())
	 *
	 *			
	 */
	public PickableItem surpriseBoxContent(SurpriseBox box){
		box.setSurpriseBoxContent(generator.nextInt(5));
		switch (box.getSurpriseBoxContent()){
			case 2: 
				return new Battery(this.getPosition().getX(), this.getPosition().getY(), 
						(((generator.nextInt(50)) + 1) * 100), box.getWeight(), this.getPosition().getBoard());
			case 3: 
				return new SurpriseBox(this.getPosition().getX(), this.getPosition().getY(),
					box.getWeight(), this.getPosition().getBoard());
			case 4: 
				return new RepairKit(this.getPosition().getX(), this.getPosition().getY(),
					(((generator.nextInt(50)) + 1) * 100), box.getWeight(), this.getPosition().getBoard());
		}
		return null;
	}
	
	
	/**
	 * This robot picks up the given item if they have the same position.
	 * 
	 * @post The item is added to the list of item held
	 * 			|new.getListOfItemHeld.contains(item)
	 * 
	 * @post The item is taken of the board.
	 * 			|!this.new.getPosition().getListOfItemsOnPosition().contains(item)
	 * 			|!this.getPosition().getBoard().listOfPickableItems(item).contains(item)
	 * 			|new.getPosition() == null
	 * 			|new.getIsOnABoard() == false
	 * 
	 * @throws IllegalArgumentException
	 * 			The given item does not exist
	 * 			|item == null
	 * @throws TerminatedObjectException
	 * 			This or the given item are terminated
	 * 			|this.isTerminated() || item.isTerminated()
	 */
	@Raw
	public void pickUp(PickableItem item) throws IllegalArgumentException, TerminatedObjectException{
		try{
			if(item == null)
				throw new IllegalArgumentException();
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(item.isTerminated())
				throw new TerminatedObjectException(item);
			if(this.getPosition().listOfItemsOnPosition.contains(item)){
				this.addItemHeld(item);
				this.getPosition().listOfItemsOnPosition.remove(item);
				this.getPosition().getBoard().getListOfItemsOnBoard().remove(item);
				item.setValidPosition(null);
				item.setIsOnABoard(false);
			}
		}
		catch(TerminatedObjectException exc){
			System.out.println("This robot is terminated and can't pick up any item");
		}
		catch(IllegalStateException exc){
			System.out.println("This item is not on a board so picking up an item is impossible");
		}
		catch(IllegalArgumentException exc){
			System.out.println("This pickableItem does not exist!");
		}
		
		
	}
	
	/**
	 * This robot drop the given item, if this item is held by the robot.
	 * 
	 * @post This item is taken from the robot and moved to the board.
	 * 			|item.new.getPosition() == this.getPosition()
	 * 			|!this.getListOfItemsHeld.contains(item)
	 * 			|item.new.isOnARobot == false
	 * 
	 * @throws IllegalArgumentException
	 * 			The given item does not exist
	 * 			|item == null
	 * @throws TerminatedObjectException
	 * 			This or the given item are terminated
	 * 			|this.isTerminated() || item.isTerminated()
	 */
	@Raw
	public void drop(PickableItem item) throws IllegalArgumentException, TerminatedObjectException{
		try{
			if(item == null)
				throw new IllegalArgumentException();
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(this.getListOfItemsHeld().contains(item)){
				item.setValidPosition(this.getPosition());
				this.getListOfItemsHeld().remove(item);
				item.setIsOnARobot(false);
			}
		}
		catch(TerminatedObjectException exc){
			
				System.out.println("This robot is terminated and can't drop any item");
		}
		catch(IllegalStateException exc){
			System.out.println("This item is not on a board so dropping an item is impossible");
		}
		catch(IllegalArgumentException exc){
			System.out.println("This pickableItem does not exist!");
		}
	}

	/**
	 * Checks whether you can put a robot at a given position
	 * 
	 * @param p
	 * 			The position to be checked.
	 * @return if the position does not excist, return true
	 * 			|if(p == null) result == true
	 * 			else if there are no other robots or wall on the position, return true.
	 * 			|result == p.hasItemOfClass(Wall.class) == false && p.hasItemOfClass(Robot.class) == false
	 */
	@Override
	public boolean canPutItemAt(Position p) {
		if(p == null)
			return true;
		else
			return p.hasItemOfClass(Wall.class) == false && p.hasItemOfClass(Robot.class) == false;
	}
	
	/**
	 * Changes the position of a robot to the given position.
	 * 
	 * @param X
	 * 			The X coordinate the new position must have.
	 * @param Y
	 * 			Tge Y coordinate the new position must have.
	 * @param board
	 * 			The board the new position is on.
	 * 
	 * @effect If there are no walls or robots on the given position, 
	 * 			the robot is removed from its former position and move to its new position.
	 * 			|!this.getPosition().getListOfItemsOnPosition().contains(this)
	 * 			|this.setValidPosition(X, Y, Board)
	 * 			Energy is decreased from the robot, taking in account the weight of items it carries
	 * 			|new.getEnergy() == 
	 * 			|	this.getEnergy().subtract(this.getEnergyForMove().add(new Energy((gram/20), EnergyUnit.Wattsecond)))
	 * 
	 * @throws TerminatedObjectException
	 * 			this is terminated or board is termrnated
	 * 			|this.isTerminated() || board.isTerminated()
	 */
	public void changeToPosition(long X, long Y, Board board) throws TerminatedObjectException{
		try{
			if(this.isTerminated())
				throw new TerminatedObjectException(this);
			if(board.isTerminated())
				throw new TerminatedObjectException(board);
			Position p = doesPositionAlreadyExist(X, Y, board);
			if((this.canPutItemAt(p))&& isValidPosition(X, Y, board) ){ 
				// zal alleen maar bewegen als er geen wall en geen robot staat!
				this.getPosition().listOfItemsOnPosition.remove(this);
				if(this.getPosition().listOfItemsOnPosition.isEmpty())	this.getPosition().terminate();
				this.setValidPosition(X, Y, board);
				double gram = 0;
				for(PickableItem item: this.getListOfItemsHeld())	
					gram += item.getWeight();
				this.decreaseEnergy(this.getEnergyForMove().add(new Energy((gram/20), EnergyUnit.Wattsecond)));
			}
		}
		catch(IllegalArgumentException exc){
			if(exc.getMessage().equals("board"))
				System.out.println("The board does not exist");
			else
				System.out.println("This robot is not on a position");
		}
		catch(TerminatedObjectException exc){
			System.out.println("This robot or the board are terminated.");
		}
	}

	/**
	 * Changes this robot to the given position.
	 * 
	 * @param position
	 * 			The position to change to.
	 * @param board
	 * 			The board the position is on.
	 * @effect |changeToPosition(position.getX(), position.getY(), board)
	 */
	public void changeToPosition(Position position, Board board){
		this.changeToPosition(position.getX(), position.getY(), board);
	}
	
	/**
	 * Returns the list of items held by this robot.
	 */
	public ArrayList<PickableItem> getListOfItemsHeld() {
		return this.listOfItemsHeld;
	}
	

	
	/**
	 * Add the given PickAble item as an item held by this robot.
	 * 
	 * @param item
	 * 			The pickAble item to add.
	 * @post If this robot can have the given pickAble item, it is added to the list of items held.
	 * 			|if(this.canHaveAsItemHeld(item))
	 *			|	then this.getListOfItemsHeld().add(item);
	 */
	public void addItemHeld(PickableItem item){
		if(this.canHaveAsItemHeld(item))
			this.getListOfItemsHeld().add(item);
	}

	/**
	 * Subtracts 4000 Wattsecond from the robot's maxEnergyOnRobot.
	 * 
	 * @effect The robot's maxEnergyOnRobot is subtracted with 4000 Wattsecond
	 * 			|new.getMaxEneryOnRobot() 
	 * 			|	== this.getMAX_ENERGY_ON_ROBOT().subtract(new Energy(4000 ,EnergyUnit.Wattsecond))
	 */
	@Override @Raw
	public void hitByLaser() {
		this.setMAX_ENERGY_ON_ROBOT(this.getMAX_ENERGY_ON_ROBOT().subtract(new Energy(4000 ,EnergyUnit.Wattsecond)));
		if(this.getMAX_ENERGY_ON_ROBOT().compareTo(getMinEnergy()) <= 0)	this.terminate();
	}
	
	/**
	 * Returns the heaviest item the robot is carrying.
	 * 
	 * @return If the list of items held by a robot is empty, null is returned.
	 * 			|if(this.getListOfItemHeld().isEmpty()
	 * 			|	result == null
	 * 		   else run through the list of item held and return the heaviest item.
	 * 			|for each item in listOfItemsHeld :
	 * 			|	if(item.getWeight() > heaviest.getWeight()
	 * 			|		heaviest = item
	 * 			|result == heaviest
	 */
	public PickableItem getHeaviestItem(){
		if(this.getListOfItemsHeld().isEmpty())
			return null;
		else{
			PickableItem heaviest = this.getListOfItemsHeld().get(0);
			for(PickableItem item : this.getListOfItemsHeld()){
				if(item.getWeight() > heaviest.getWeight())
					heaviest = item;
			}
			return heaviest;
		}
			
	}
	
	/**
	 * Transfers the items held by this robot to the given robot.
	 * 
	 * @param robot
	 * 			The robot to transfer to.
	 * @pre The robots must be next to each other in order, else the method is not executed.
	 * 			|this.isNextTo(robot)
	 * @post This robot's item held are all added the the given robot's items and taken of this robot.
	 * 			|new.robot.getListOfItemsHeld().containsAll(this.getListOfItemsHeld());
	 * 			|new.getListOfItemsHeld().isEmpty();
	 */
	public void transferTo(Robot robot){
		if(this.isNextTo(robot)){
			robot.getListOfItemsHeld().addAll(this.getListOfItemsHeld());
			this.getListOfItemsHeld().clear();
		}
	}
	
	@Override
	public boolean hasAtleast(Object condition){
		if(condition.getClass() == Energy.class){
			if((this.getEnergy().compareTo((Energy)condition)) >= 0)
					return true;
		}
		else if(condition.getClass() == Position.class){
			if((this.getPosition() == ((Position)condition)))
				return true;
		}
		return false;
	}
	
	/**
	 * Returns the position that is to the right of this robot taking into account the orientation of the robot.
	 * 
	 * @param orientation	
	 * 			The orientation to take in account
	 * @return Return the position at the right.
	 * 			|return new Position(new.getPosition().getX(), tnew.getPosition().getY(), new.getPosition().getBoard())
	 */
	public Position getRightNeighbour(Orientation orientation){
		switch(orientation){
		case UP:
			return new Position(this.getPosition().getX()+1, this.getPosition().getY(), this.getPosition().getBoard());
		case RIGHT:
			return new Position(this.getPosition().getX(), this.getPosition().getY()+1, this.getPosition().getBoard());
		case DOWN:
			return new Position(this.getPosition().getX()-1, this.getPosition().getY(), this.getPosition().getBoard());
		case LEFT:
			return new Position(this.getPosition().getX(), this.getPosition().getY()-1, this.getPosition().getBoard());
		}
		return null;
	}
	
    /**
     * Set the robot's program to the given program.
     * 
     * @param program
     * 			...
     * @throws IllegalStateException		
     * 			...
     * 			|program == null
     * @throws TerminatedObjectException
     * 			...
     * 			|this.isTerminated()
     */
    public void setProgram(Program program) throws IllegalArgumentException, TerminatedObjectException{
        try {    
        	if(this.isTerminated())
        		throw new TerminatedObjectException(this);
        	if(program == null)
        		throw new IllegalArgumentException();
        	resetLastExecutedCommand();
        	this.program = program;   
        }
        catch(IllegalStateException exc) {
        	System.out.println("Cannot appoint program to terminated robot");
        }
        catch(IllegalArgumentException exc) {
        	System.out.println("The program does not excist!");
        }
    }
   
    
    /**
     * Return the robot's program
     */
    @Basic
    public Program getProgram() {
            return program;
    }
    
    /**
     * This robot executes the program it holds and executes one single basic step.
     * 
     * @effect The next basic command is executed.
     * 			|newCommand.execute(this)
     * 
     * @throws IllegalStateException
     * 			...
     * 			|this.getProgram() == null || this.getPosition() == null
     * @throws TerminatedObjectException
     * 			...
     * 			|this.isTerminated()
     */
    public void executeNextBasicCommand() throws TerminatedObjectException{
       try {    
    		if (this.getProgram() == null)
                    throw new IllegalStateException("program");
            if (this.isTerminated())
                    throw new TerminatedObjectException(this);
            if (this.getPosition() == null)
                    throw new IllegalStateException();
            BasicCommand newCommand = getProgram().getNextBasicCommand(getLastCommandLine(), this);
            newCommand.execute(this);
            lastCommandLine = newCommand.getLineNb();
       }
       catch(IllegalStateException exc) {
    	   if(exc.getMessage() == "program")
    		   System.out.println("This robot does not have a program");
    	   else	
    		   System.out.println("This robot has no position");
       }
       catch(TerminatedObjectException exc) {
    	   System.out.println("This robot is terminated and can't execute progs!");
       }
       catch(EndOfProgramException exc) {
    	   System.out.println("The program has ended");
       }
    }
    
    private int lastCommandLine;
    
    /**
     * Returns the last commandLine this robot has executed.
     * @return
     */
    public int getLastCommandLine() {
            return lastCommandLine;
    }
    
    /**
     * Reset the line number of the last command line to zero.
     * 
     * @post        The line number of the last executed command is set to zero.
     *                      | new.getLastCommandLine() == 0
     */
    public void resetLastExecutedCommand() {
            lastCommandLine= 0;
    }
}